
// Server (responding) side parts of "big data" transfers.

package async

import (
	"syscall"
	"unsafe"
)

type ShareIn struct {
	*Call
	size int
}

// To beat overflow checks.
func sysarg(rc syscall.Errno) syscall.Arg {
	return syscall.Arg(rc)
}

// This function is to be used from within a connection fibril.
func ShareInReceive() (rc syscall.Errno, result ShareIn, size int) {
	var callid syscall.IpcCallidT
	var sz syscall.SizeT

	success := async_share_in_receive(&callid, &sz)
	if !success {
		rc = syscall.ENOENT
	} else {
		// Protect against overflow.
		if int(sz) < 0 || uint64(sz) != uint64(int(sz)) {
			ipc_answer_fast(callid, sysarg(syscall.ERANGE), 0, 0, 0, 0)
			rc = syscall.ERANGE
			return
		}

		result = ShareIn{&Call{id: callid}, int(sz)}
		size = int(sz)
	}
	return
}

func (s ShareIn) Finalize(area syscall.MemArea, flags uint) syscall.Errno {
	if area.Size() < s.size {
		return syscall.EINVAL
	}
	rc := async_share_in_finalize(s.Call.id, &area.Slice()[0], uint(flags))
	return syscall.Errno(rc)
}

////////////////////////////////////////////////////////////////////////////////////////////////////

type ShareOut struct {
	*Call
	size int
	flags uint
}

// This function is to be used from within a connection fibril.
func ShareOutReceive() (rc syscall.Errno, result ShareOut, size int, flags uint) {
	var callid syscall.IpcCallidT
	var sz syscall.SizeT
	success := async_share_out_receive(&callid, &sz, &flags)
	if !success {
		rc = syscall.ENOENT
	} else {
		// Protect against overflow.
		if int(sz) < 0 || uint64(sz) != uint64(int(sz)) {
			ipc_answer_fast(callid, sysarg(syscall.ERANGE), 0, 0, 0, 0)
			rc = syscall.ERANGE
			return
		}

		result = ShareOut{&Call{id: callid}, int(sz), flags}
		size = int(sz)
	}
	return
}

func (s ShareOut) Finalize() (syscall.Errno, *syscall.MemArea) {
	var dst *byte

	rc := async_share_out_finalize(s.Call.id, &dst)
	if rc == 0 {
		return syscall.Errno(rc), nil
	}

	return syscall.EOK, syscall.UnsafeCreateArea(unsafe.Pointer(dst), s.size, s.flags)
}

////////////////////////////////////////////////////////////////////////////////////////////////////

type DataRead struct {
	*Call
	maxsize int
}

func DataReadReceive() (rc syscall.Errno, result DataRead, maxsize int) {
	var callid syscall.IpcCallidT
	var sz syscall.SizeT

	success := async_data_read_receive(&callid, &sz)
	if !success {
		rc = syscall.ENOENT
		return
	}

	// Protect against overflow.
	if int(sz) < 0 || uint64(sz) != uint64(int(sz)) {
		ipc_answer_fast(callid, sysarg(syscall.ERANGE), 0, 0, 0, 0)
		rc = syscall.ERANGE
		return
	}

	result.Call = &Call{id: callid}
	result.maxsize = int(sz)
	maxsize = int(sz)
	return
}

func (r DataRead) Finalize(data []byte) syscall.Errno {
	if len(data) > r.maxsize {
		return syscall.EINVAL
	}

	rc := async_data_read_finalize(r.Call.id, &data[0], syscall.SizeT(len(data)))
	return syscall.Errno(rc)
}

func DataReadForward(exch *Exchange, method, arg1, arg2, arg3, arg4 syscall.Arg) (rc syscall.Errno, ret1, ret2, ret3, ret4, ret5 syscall.Arg) {
	var cdata callData
	ret := async_data_read_forward_fast(exch.ptr, method, arg1, arg2, arg3, arg4, &cdata)
	return syscall.Errno(ret), cdata.args[1], cdata.args[2], cdata.args[3], cdata.args[4], cdata.args[5]
}

////////////////////////////////////////////////////////////////////////////////////////////////////

type DataWrite struct {
	*Call
}

func DataWriteReceive() (rc syscall.Errno, result DataWrite, size int) {
	var callid syscall.IpcCallidT
	var sz syscall.SizeT
	success := async_data_write_receive(&callid, &sz)
	if !success {
		rc = syscall.ENOENT
		return
	}

	// Protect against overflow.
	if int(sz) < 0 || uint64(sz) != uint64(int(sz)) {
		ipc_answer_fast(callid, sysarg(syscall.ERANGE), 0, 0, 0, 0)
		rc = syscall.ERANGE
		return
	}

	result.Call = &Call{id: callid}
	size = int(sz)
	return
}

func (w DataWrite) Finalize(buf []byte) syscall.Errno {
	var bufptr *byte
	if len(buf) == 0 {
		bufptr = nil
	} else {
		bufptr = &buf[0]
	}
	rc := async_data_write_finalize(w.Call.id, bufptr, syscall.SizeT(len(buf)))
	return syscall.Errno(rc)
}

func DataWriteVoid(retval syscall.Arg) {
	async_data_write_void(retval)
}

func DataWriteForward(exch *Exchange, method, arg1, arg2, arg3, arg4 syscall.Arg) (rc syscall.Errno, ret1, ret2, ret3, ret4, ret5 syscall.Arg) {
	var cdata callData
	ret := async_data_write_forward_fast(exch.ptr, method, arg1, arg2, arg3, arg4, &cdata)
	return syscall.Errno(ret), cdata.args[1], cdata.args[2], cdata.args[3], cdata.args[4], cdata.args[5]
}
